﻿/******************************************************************************
* SunnyUI.FrameDecoder 开源TCP、串口数据解码库。
* CopyRight (C) 2022-2023 ShenYongHua(沈永华).
* QQ群：56829229 QQ：17612584 EMail：SunnyUI@qq.com
*
* Blog:   https://www.cnblogs.com/yhuse
* Gitee:  https://gitee.com/yhuse/SunnyUI.FrameDecoder
*
* SunnyUI.FrameDecoder.dll can be used for free under the MIT license.
* If you use this code, please keep this note.
* 如果您使用此代码，请保留此说明。
******************************************************************************
* 文件名称: DataCache.cs
* 文件说明: 流式数据缓存
* 当前版本: V4.0
* 创建日期: 2023-01-28
*
* 2023-01-28: V4.0.0 增加文件说明
******************************************************************************/

using System;
using System.Buffers;
using System.Diagnostics;

namespace Sunny.FrameDecoder
{
    /// <summary>
    /// 数据缓存
    /// </summary>
    /// <typeparam name="T">类型</typeparam>
    public class DataCache<T> : IDataCache<T>, IBufferWriter<T>
    {
        private T[] _buffer;
        private T[] _temp;
        private int _index = 0;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="capacity">初始容量</param>
        public DataCache(int capacity = ConstDefine.CacheSize)
        {
            if (capacity <= 0) capacity = ConstDefine.CacheSize;

            _buffer = ArrayPool<T>.Shared.Rent(capacity);
            _temp = ArrayPool<T>.Shared.Rent(capacity);
        }

        /// <summary>
        /// 析构函数
        /// </summary>
        public void Dispose()
        {
            ArrayPool<T>.Shared.Return(_buffer);
            ArrayPool<T>.Shared.Return(_temp);
        }

        /// <summary>
        /// 标签
        /// </summary>
        public object Tag { get; set; }

        private void CheckAndResizeBuffer(int sizeHint)
        {
            if (sizeHint + _index > _buffer.Length)
            {
                int len = _buffer.Length * 2;
                while (len < sizeHint + _index)
                {
                    len = len * 2;
                }

                T[] tmp = ArrayPool<T>.Shared.Rent(len);
                _buffer.CopyTo(tmp, 0);
                ArrayPool<T>.Shared.Return(_buffer);
                _buffer = tmp;

                ArrayPool<T>.Shared.Return(_temp);
                _temp = ArrayPool<T>.Shared.Rent(len);
            }
        }

        /// <inheritdoc/>        
        public void Advance(int count)
        {
            if (count < 0)
                throw new ArgumentException(null, nameof(count));

            if (_index > _buffer.Length - count)
                ThrowHelper.ThrowInvalidOperationException_AdvancedTooFar(_buffer.Length);

            _index += count;
        }

        /// <inheritdoc/>      
        public void Clear()
        {
            _index = 0;
            _buffer.AsSpan().Clear();
        }

        /// <inheritdoc/>      
        public Memory<T> GetMemory(int sizeHint = 0)
        {
            CheckAndResizeBuffer(sizeHint);
            Debug.Assert(_buffer.Length > _index);
            return _buffer.AsMemory(_index);
        }

        /// <inheritdoc/>      
        public Span<T> GetSpan(int sizeHint = 0)
        {
            CheckAndResizeBuffer(sizeHint);
            Debug.Assert(_buffer.Length > _index);
            return _buffer.AsSpan(_index);
        }

        /// <inheritdoc/>      
        public void Remove(int length)
        {
            if (length <= 0) return;
            if (length == _index)
            {
                Clear();
            }
            else
            {
                _buffer.AsSpan().Slice(length, _index - length).CopyTo(_temp.AsSpan());
                _index -= length;

                _buffer.AsSpan().Clear();
                _temp.AsSpan().Slice(0, _index).CopyTo(_buffer.AsSpan());
            }
        }

        /// <summary>
        /// 返回到目前为止写入基础缓冲区的数据.
        /// </summary>
        public ReadOnlyMemory<T> WrittenMemory => _buffer.AsMemory(0, _index);

        /// <summary>
        /// 返回到目前为止写入基础缓冲区的数据
        /// </summary>
        public ReadOnlySpan<T> WrittenSpan => _buffer.AsSpan(0, _index);

        /// <summary>
        /// 返回到目前为止写入基础缓冲区的数据量
        /// </summary>
        public int WrittenCount => _index;

        /// <summary>
        /// 返回基础缓冲区内的总空间量
        /// </summary>
        public int Capacity => _buffer.Length;

        /// <summary>
        /// 返回在不强制基础缓冲区增长的情况下仍可写入的可用空间量
        /// </summary>
        public int FreeCapacity => _buffer.Length - _index;

        /// <summary>
        /// 增加数据
        /// </summary>
        /// <param name="value">数据</param>
        public void Add(ReadOnlySpan<T> value)
        {
            var span = GetSpan(value.Length);
            value.CopyTo(span);
            Advance(value.Length);
        }
    }
}
